﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;
using System.IO;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
using System.Diagnostics;
using System.Globalization;




namespace Bluefly
{



    public class VarioService : VarioServiceBase, IVarioService
    {

        private StreamSocket socket;
        Guid SERVICE_GUID = Guid.Parse("00001101-0000-1000-8000-00805F9B34FB");
        const string deviceSearchString = "blueflyvario";


        public VarioService()
            : base()
        {

        }

        public override async void Run()
        {

            setState(ConnectionState.STATE_CONNECTING);

            //needs to run  on ui thread. will fail if called from background thread on first call as it will prompt user to accept device usage.
            await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
             {
                 try
                 {

                     RfcommDeviceService service = null;

                     var alldevices = await DeviceInformation.FindAllAsync();

                     var blueflydevices = alldevices.Where(c => !string.IsNullOrEmpty(c.Name) && c.Name.ToLower().StartsWith(deviceSearchString.ToLower())).ToList();

                     foreach (var device in blueflydevices)
                     {
                         try
                         {
                             service = await RfcommDeviceService.FromIdAsync(device.Id);
                             break;
                         }
                         catch
                         {

                         }

                     }

                     Connect(service);
                 }
                 catch (Exception ex)
                 {
                     AddError(ex.Message);
                     setState(ConnectionState.STATE_ERROR);
                 }

             });
        }


        void Connect(RfcommDeviceService service)
        {

            System.Threading.Tasks.Task.Factory.StartNew(async () =>
            {
                try
                {
                    if (service == null)
                    {
                        AddError("No BlueflyVario device found");
                        setState(ConnectionState.STATE_ERROR);
                        return;
                    }

                    setUpData();

                    lock (this)
                    {
                        socket = new StreamSocket();
                    }

                    await socket.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName, SocketProtectionLevel.PlainSocket);

                    _isConnected = true;

                    setState(ConnectionState.STATE_CONNECTED);

                    ListenForData(socket);

                    setState(ConnectionState.STATE_NONE);

                }
                catch (Exception ex)
                {

                    _isConnected = false;
                    AddError(ex.Message);    
                    setState(ConnectionState.STATE_ERROR);

                }
                finally
                {

                    Disconnect();
                    _isConnected = false;

                }
            });
        }

        protected override void Disconnect()
        {
            lock (this)
            {
                if (socket != null)
                {
                    socket.Dispose();
                    socket = null;
                }
            }
        }


        void ListenForData(StreamSocket socket)
        {

            try
            {
                using (StreamReader reader = new StreamReader(socket.InputStream.AsStreamForRead(), UTF8Encoding.UTF8))
                {
                    while (_isConnected)
                    {

                        string line = reader.ReadLine();
                        OnDeviceSentenceReceived(line);
                    }
                }
            }
            catch (Exception ex)
            {
                AddError(ex.Message);
                setState(ConnectionState.STATE_ERROR);
            }
        }


        protected override void OnDeviceSentenceReceived(string line)
        {

            try
            {
                if (string.IsNullOrEmpty(line) || Current == null)
                {
                    return;
                }

                String[] split = line.Split(' ');

                if (string.IsNullOrEmpty(split[0]))
                {
                    return;
                }

                if (split[0] == "PRS")
                {

                    ProcessPressureUpdate(int.Parse(split[1], NumberStyles.HexNumber), split.Length > 2 ? int.Parse(split[2], NumberStyles.HexNumber) : default(int?));
                    RaisePressureChanged(_rawPressure, _dampedAltitude, _rawAltitude, _dampedrate, _kalmanrate);

                }
                else if (split[0] == ("TMP"))
                {
                    this.lastUpdateType = UpdateType.TMP;
                    updateTemperature(int.Parse(split[1]));
                    RaiseTemperatureChanged(int.Parse(split[1]));
                }

                else if (split[0] == "BAT")
                {

                    this.lastUpdateType = UpdateType.BAT;
                    int bat = int.Parse(split[1], NumberStyles.HexNumber);     //bat is in mV
                    RaiseBatteryVoltageChanged((double)chargeFromVolts.getValue(bat / 1000.0));

                }
                else if (split[0] == "BFV")
                {

                    this.lastUpdateType = UpdateType.VER;
                    try
                    {
                        int ver = int.Parse(split[1]);
                        setHardwareVersion(ver);
                    }
                    catch (Exception)
                    {
                        setHardwareVersion(0);
                    }

                }
                else if (split[0] == "BST")
                {
                    this.lastUpdateType = UpdateType.KEYS;
                    updateHardwareSettingsKeys(line);

                }
                else if (split[0] == "SET")
                {
                    this.lastUpdateType = UpdateType.VALUES;
                    updateHardwareSettingsValues(line);
                }
            }
            catch (Exception ex)
            {  
                //log  ex
                return;
            }

        }


    }

}
